package com.gitee.wsl.ext.string

private const val MIN_SUPPLEMENTARY_CODE_POINT = 0x010000

private const val SURROGATE_OFFSET = MIN_SUPPLEMENTARY_CODE_POINT - (Char.MIN_HIGH_SURROGATE.code shl 10) - Char.MIN_LOW_SURROGATE.code


/**
 * Writes the UTF8 bytes of String to a [writer].
 * @throws IllegalArgumentException when string contains invalid UTF-16 unpaired surrogates
 */
fun String.writeUTF8Bytes(writer: (byte: Byte) -> Unit) {
    var i = 0
    while (i < length) {
        val char = this[i]
        when {
            char.code < 0x80 -> writer(char.code.toByte())
            char.code < 0x800 -> {
                writer((0xC0 or (char.code ushr 6)).toByte())
                writer((0x80 or (0x3F and char.code)).toByte())
            }
            char < Char.MIN_SURROGATE || char > Char.MAX_SURROGATE -> {
                writer((0xE0 or (char.code ushr 12)).toByte())
                writer((0x80 or (0x3F and (char.code ushr 6))).toByte())
                writer((0x80 or (0x3F and char.code)).toByte())
            }
            else -> {
                val low = this[++i]
                if (i == this.length || !(char.isHighSurrogate() && low.isLowSurrogate())) {
                    throw IllegalArgumentException("Unpaired surrogate at index: ${i - 1}")
                }

                val codePoint = (char.code shl 10) + low.code + SURROGATE_OFFSET
                writer((0xF0 or (codePoint ushr 18)).toByte())
                writer((0x80 or (0x3F and (codePoint ushr 12))).toByte())
                writer((0x80 or (0x3F and (codePoint ushr 6))).toByte())
                writer((0x80 or (0x3F and codePoint)).toByte())
            }
        }
        i++
    }
}